package com.keqiang.chart.impl.axis.impl.pseudo3d.combined

import android.animation.ValueAnimator
import android.graphics.Canvas
import android.view.MotionEvent
import com.keqiang.chart.animation.Animator
import com.keqiang.chart.base.BaseChartView
import com.keqiang.chart.base.IDataChangeObserver
import com.keqiang.chart.base.ITouchHandler
import com.keqiang.chart.impl.axis.Axis
import com.keqiang.chart.impl.axis.BaseAxisChart3D
import com.keqiang.chart.impl.axis.BaseXAxis
import com.keqiang.chart.impl.axis.BaseYAxis
import com.keqiang.chart.impl.axis.XAxis3D
import com.keqiang.chart.impl.axis.YAxis
import com.keqiang.chart.impl.axis.impl.combined.ChartType
import com.keqiang.chart.impl.axis.impl.combined.CombinedData
import com.keqiang.chart.impl.axis.impl.combined.YAxisType
import com.keqiang.chart.impl.axis.impl.line.LineChart
import com.keqiang.chart.impl.axis.impl.pseudo3d.bar.BarChart3D
import com.keqiang.chart.impl.axis.render.IDataRender
import com.keqiang.chart.impl.axis.render.XAxisRender
import com.keqiang.chart.utils.toRadian
import kotlin.math.cos
import kotlin.math.sin

/**
 * 折线图和柱状图组合图形
 *
 * @author Created by wanggaowan on 2022/7/29 13:09
 */
open class CombinedChart3D constructor(
    view: BaseChartView<*>
) : BaseAxisChart3D<CombinedData>(view) {

    init {
        super.addDataChangeObserver(object : IDataChangeObserver {
            override fun onChange() {
                lineChar.data = data?.lineData?.data
                if (yAxisEnd != null && data?.lineData?.yAxisType == YAxisType.END) {
                    lineChar.yAxis = yAxisEnd!!
                } else {
                    lineChar.yAxis = yAxis
                }

                barChar.data = data?.barData?.data
                if (yAxisEnd != null && data?.barData?.yAxisType == YAxisType.END) {
                    barChar.yAxis = yAxisEnd!!
                } else {
                    barChar.yAxis = yAxis
                }
            }
        })
    }

    private var mAnimDrawChart: IntArray? = null

    override var xAxis: XAxis3D = XAxis3D()

    override var yAxis: YAxis = YAxis()

    /**
     * 右侧Y轴
     */
    var yAxisEnd: YAxis? = null

    var lineChar: LineChart = LineChart(view).apply {
        xAxis = this@CombinedChart3D.xAxis
        yAxis = this@CombinedChart3D.yAxis
    }
        private set

    var barChar: BarChart3D = BarChart3D(view).apply {
        xAxis = this@CombinedChart3D.xAxis
        yAxis = this@CombinedChart3D.yAxis
    }
        private set

    /**
     * 以哪个图表作为主表，[CombinedChart3D]的X轴，Y轴将与主表一致。默认为[ChartType.BAR]
     */
    @setparam:ChartType
    var mainChart: Int = ChartType.BAR

    override var yAxisRender: Combined3DYAxisRender<CombinedChart3D> = Combined3DYAxisRender(this)

    override var xAxisRender: XAxisRender<CombinedChart3D> =
        object : XAxisRender<CombinedChart3D>(this) {
            override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float,
                                gridWidth: Float, labelMaxHeight: Float, yAxisRange: Float) {
                if (mainChart == ChartType.BAR) {
                    barChar.xAxisRender.onDraw(canvas, start, top, end, bottom, gridWidth, labelMaxHeight, yAxisRange)
                } else {
                    // 折线图作为主图时，轴线的粗细相当于线条的高度
                    val lineWidth = xAxis.lineWidth
                    xAxis.lineWidth = xAxis.lineHeight
                    lineChar.xAxisRender.onDraw(canvas, start, top, end, bottom, gridWidth, labelMaxHeight, yAxisRange)
                    xAxis.lineWidth = lineWidth
                }
            }
        }

    override var dataRender: IDataRender = object : IDataRender {
        override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float,
                            gridWidth: Float, gridHeight: Float, animator: Animator?) {
            data?.let {
                it.getDrawOrder().forEach { order ->
                    if (order == ChartType.BAR) {
                        val width: Float
                        val height: Float
                        if (mainChart == ChartType.BAR) {
                            width = gridWidth
                            height = gridHeight
                        } else {
                            // 主轴是折线图，此时柱状图网格为标签数 - 1
                            val count = xAxis.labelCount
                            xAxis.labelCount = xAxis.labelCount - 1
                            width = barChar.getGridWidth(xAxis, end - start)
                            height = barChar.getGridHeight(yAxis, bottom - top)
                            xAxis.labelCount = count
                        }

                        if (chartNeedAnimDraw(ChartType.BAR)) {
                            barChar.dataRender.onDraw(canvas, start, top, end, bottom, width, height, animator)
                        } else {
                            barChar.dataRender.onDraw(canvas, start, top, end, bottom, width, height, null)
                        }
                    } else if (order == ChartType.LINE) {
                        val width: Float
                        val height: Float
                        if (mainChart == ChartType.LINE) {
                            width = gridWidth
                            height = gridHeight
                        } else {
                            // 主轴是柱状图，此时折线图网格为标签数 + 1
                            val count = xAxis.labelCount
                            xAxis.labelCount = count + 1
                            width = lineChar.getGridWidth(xAxis, end - start)
                            height = lineChar.getGridHeight(yAxis, bottom - top)
                            xAxis.labelCount = count
                        }

                        val xOffset = xAxis.lineWidth * cos(faceDegrees.toRadian)
                        if (chartNeedAnimDraw(ChartType.LINE)) {
                            lineChar.dataRender.onDraw(canvas, start + xOffset, top, end + xOffset, bottom, width, height, animator)
                        } else {
                            lineChar.dataRender.onDraw(canvas, start + xOffset, top, end + xOffset, bottom, width, height, null)
                        }
                    }
                }
            }
        }
    }

    private fun chartNeedAnimDraw(chart: Int): Boolean {
        if (mAnimDrawChart == null) {
            return true
        }

        for (value in mAnimDrawChart!!) {
            if (value == chart) {
                return true
            }
        }

        return false
    }

    override var touchHandler: ITouchHandler? = object : ITouchHandler {

        override fun onTouch(event: MotionEvent): Boolean {
            if (data == null) {
                return false
            }

            var doTouch = false
            data?.let {
                if (!it.lineData?.data.isNullOrEmpty()) {
                    doTouch = lineChar.touchHandler?.onTouch(event) ?: false
                }

                if (it.barData != null) {
                    val touch = barChar.touchHandler?.onTouch(event) ?: false
                    if (!doTouch) {
                        doTouch = touch
                    }
                }
            }

            if (doTouch) {
                this@CombinedChart3D.view.invalidate()
            }
            return doTouch
        }
    }

    open var highLightRender: CombinedChart3DHighLightRender? = null
        set(value) {
            field = value
            lineChar.highLightRender = value?.lineChartRender
            barChar.highLightRender = value?.barChartRender
        }

    override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float) {
        initAxisLabel(yAxis)
        initAxisLabel(xAxis)
        yAxisEnd?.also { initAxisLabel(it) }

        // y轴绘制文本的最大宽度
        val yMaxTextWidth = getYAxisTextMaxWidth(yAxis)
        val yMaxTextWidthEnd: Float = if (yAxisEnd == null) 0f else getYAxisTextMaxWidth(yAxisEnd!!)
        // x轴轴线左边起始位置
        var xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis
        xAxisStart -= xAxis.lineWidth * cos(faceDegrees.toRadian)
        if (xAxisStart < 0f) {
            xAxisStart = 0f
        }

        // x轴轴线右边结束位置
        var xAxisEnd =
            end - yMaxTextWidthEnd - if (yAxisEnd == null) 0f else yAxisEnd!!.labelOffsetAxis
        xAxisEnd -= xAxis.lineWidth * cos(faceDegrees.toRadian)

        // 网格宽度，x轴文本绘制的最大宽度
        val gridWidth =
            getGridWidth(xAxis, xAxisEnd - xAxisStart - xAxis.startPadding - xAxis.endPadding)
        // x轴文本绘制的最大高度
        val xLabelMaxHeight =
            getXAxisTextMaxHeight(xAxis, gridWidth, start, end, xAxisStart, xAxisEnd)
        // x轴轴线以下部分绘制内容高度结束位置
        val xAxisBottom = bottom - xAxis.labelOffsetAxis - xLabelMaxHeight
        // x轴轴线y轴坐标
        val xAxisTop = xAxisBottom - xAxis.lineHeight

        // y轴轴线长度
        var yRange = xAxisTop - top
        // 减去底部矩形宽度导致的高度减少部分
        yRange -= sin(faceDegrees.toRadian) * xAxis.lineWidth

        // 网格高度，y轴文本绘制的最大高度，
        val gridHeight = getGridHeight(yAxis, yRange - yAxis.endPadding - yAxis.startPadding)

        xAxisRender.onDraw(canvas, xAxisStart, xAxisTop, xAxisEnd, xAxisBottom, gridWidth, xLabelMaxHeight, yRange)

        yAxisRender.onDraw(canvas, xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop, yMaxTextWidth, gridHeight, xAxisEnd - xAxisStart)
        yAxisEnd?.also {
            yAxisRender.onDrawEnd(canvas, xAxisEnd - it.lineWidth, top, xAxisEnd, xAxisTop, yMaxTextWidthEnd, gridHeight, xAxisEnd - xAxisStart)
        }

        dataRender.onDraw(canvas, xAxisStart + yAxis.lineWidth + xAxis.startPadding,
            top + yAxis.endPadding, xAxisEnd - xAxis.endPadding - if (yAxisEnd == null) 0f else yAxisEnd!!.lineWidth,
            xAxisTop - yAxis.startPadding, gridWidth, gridHeight, animator)
    }

    override fun addDataChangeObserver(observer: IDataChangeObserver) {
        lineChar.addDataChangeObserver(observer)
        barChar.addDataChangeObserver(observer)
    }

    override fun removeDataChangeObserver(observer: IDataChangeObserver) {
        lineChar.removeDataChangeObserver(observer)
        barChar.removeDataChangeObserver(observer)
    }

    override fun initAxisLabel(axis: Axis) {
        if (mainChart == ChartType.BAR) {
            barChar.initAxisLabel(axis)
            return
        }

        if (mainChart == ChartType.LINE) {
            lineChar.initAxisLabel(axis)
            return
        }

        super.initAxisLabel(axis)
    }

    override fun getGridWidth(xAxis: BaseXAxis, xAxisLength: Float): Float {
        if (mainChart == ChartType.BAR) {
            return barChar.getGridWidth(xAxis, xAxisLength)
        }

        if (mainChart == ChartType.LINE) {
            return lineChar.getGridWidth(xAxis, xAxisLength)
        }

        return super.getGridWidth(xAxis, xAxisLength)
    }

    override fun getGridHeight(yAxis: BaseYAxis, yAxisLength: Float): Float {
        if (mainChart == ChartType.BAR) {
            return barChar.getGridHeight(yAxis, yAxisLength)
        }

        if (mainChart == ChartType.LINE) {
            return lineChar.getGridHeight(yAxis, yAxisLength)
        }

        return super.getGridHeight(yAxis, yAxisLength)
    }

    override fun animDraw(duration: Long) {
        mAnimDrawChart = null
        super.animDraw(duration, null, null)
    }

    override fun animDraw(duration: Long, updateListener: ValueAnimator.AnimatorUpdateListener?) {
        mAnimDrawChart = null
        super.animDraw(duration, updateListener, null)
    }

    override fun animDraw(duration: Long,
                          updateListener: ValueAnimator.AnimatorUpdateListener?,
                          listener: android.animation.Animator.AnimatorListener?) {
        mAnimDrawChart = null
        super.animDraw(duration, updateListener, listener)
    }

    /**
     * 以动画的形式进行绘制,[chart]指定需要动画绘制的图表，未指定则直接绘制
     */
    @JvmOverloads
    fun animDraw(duration: Long, @ChartType vararg chart: Int,
                 updateListener: ValueAnimator.AnimatorUpdateListener? = null,
                 listener: android.animation.Animator.AnimatorListener? = null) {
        mAnimDrawChart = chart
        super.animDraw(duration, updateListener, listener)
    }

    override fun isEmptyData(): Boolean {
        return data == null || (data?.barData?.data == null && data?.lineData?.data.isNullOrEmpty())
    }
}